/**	servlet is the entrance of program
 * system core servlet,it contains admin login/logout/query admin account
 * servlet layer implements verification of parameter
 * response encapsulated in business module due to content maybe text or image
 * by fuzhou huilin corp. ltd @author lms 2016.07.18
 */
package com.fzrskj.cloud.free.thridparty.pay.sft;

import com.fzrskj.cloud.free.core.enums.DicParamEnum;
import com.fzrskj.cloud.free.core.tool.CCookieTool;
import com.fzrskj.cloud.free.core.tool.CJsonTool;
import com.fzrskj.cloud.free.core.utils.CloudContextUtils;
import com.fzrskj.cloud.free.core.utils.DoubleUtil;
import com.fzrskj.cloud.free.dic.utils.DicParamUtils;
import com.fzrskj.cloud.free.user.bean.UserCharge;
import com.fzrskj.cloud.free.user.utils.FuncHandleUtils;
import com.fzrskj.cloud.free.user.utils.UserChargeUtils;
import com.fzrskj.cloud.free.user.utils.UserLogUtils;
import com.winiis.component.utils.client.RestClientUtils;
import ms.core.db.ConnectionContext;
import ms.core.tool.StrTool;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

public class ShenpayNotifyServlet extends HttpServlet {

	//servlet serial id
	private static final long serialVersionUID = 2000L;

	//constructor function, you can do some initialize here
	public ShenpayNotifyServlet() {
		super();
	}

	//Destruction of the servlet. <br>
	@Override
	public void destroy() {
		super.destroy(); // Just puts "destroy" string in log
		// Put your code here
	}

	/**the entrance of system servlet with get method
	 * This method is called when a form has its tag value method equals to get.
	 * @param request the request send by the client to the server
	 * @param response the response send by the server to the client
	 * @throws ServletException if an error occurred
	 * @throws IOException if an error occurred
	 */
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request,response);
	}

	boolean canContinueDealOrder = true;
	Integer agentId = 0;
	String bizOrderId = "";

	/**
	 * The doPost method of the servlet. <br>
	 * This method is called when a form has its tag value method equals to post.
	 * 
	 * @param request the request send by the client to the server
	 * @param response the response send by the server to the client
	 * @throws ServletException if an error occurred
	 * @throws IOException if an error occurred
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {

			request.setCharacterEncoding("UTF-8");

			//获得参数
			Map<String, String[]> params = request.getParameterMap();
			Map<String, String> map = new HashMap<String, String>();
			String queryString = "";
			String torderid = "";

			for (String key : params.keySet()) {
				String[] values = params.get(key);
				for (int i = 0; i < values.length; i++) {
					String value = new String(values[i].getBytes("ISO-8859-1"), "UTF-8");
					key = new String(key.getBytes("ISO-8859-1"), "UTF-8");

					//获取订单号
					if (key.equals("Ext1")) torderid = value;

					//传给入账的参数
					map.put(key, value);

					queryString += key + "=" + value + "&";
				}
			}
			String sid = CCookieTool.getSessionId(request);
			CCookieTool.refreshSessionId(request, response, sid);

			// 去掉最后一个&
			if (queryString.length() > 10) {
				queryString = queryString.substring(0, queryString.length() - 1);
			}

			//记录日志
			UserLogUtils.addLog(this, CCookieTool.getUid(request), "收到盛付通请帐求:【" + queryString + "】");

			//处理通知
			String content = this.spayReceive(map);
			String code = CJsonTool.getStrValue(content, "code", null);

			//充值成功，处理订单
			if (torderid.length() > 1 && code.equals("OK") && this.canContinueDealOrder) {
				FuncHandleUtils hand = new FuncHandleUtils();
				String[] orders = torderid.split(",");
				for (int i = 0; i < orders.length; i++) {
					hand.HandleOnLine(orders[i]);
				}
				hand = null;
			}


			//通知订单发起代理，触发充值关联订单处理操作
			if (!StringUtils.isBlank(torderid) && !this.canContinueDealOrder) {
				String webUrl = DicParamUtils.getParamValueByAgentIdAndParamName("weburl", this.agentId);
				webUrl += (webUrl.endsWith("/") ? "" : "/") + "pay_success?type=alipay&orderId=" + torderid;

				RestClientUtils clientUtils = new RestClientUtils();
				String noticeResponse = clientUtils.get(webUrl);
				UserLogUtils.addLog(this, CloudContextUtils.getAdminId(), "通知代理充值结果，请求地址：[" +
						webUrl + "]，映应结果：[" + noticeResponse + "]");
			}

			//输出到页面
			response.setContentType("text/html ");
			response.setCharacterEncoding("UTF-8");
			PrintWriter out = response.getWriter();
			out.println(code);

		} finally {
			ConnectionContext.closeAllConnection();
		}
	}

	/**Initialization of the servlet. <br>
	 * @throws ServletException if an error occurs
	 */
	@Override
	public void init() throws ServletException {
		//TODO Put your code here
	}
	
	/**
	 * 网银通知
	 * @return
	 * @throws UnsupportedEncodingException 
	 */
	public  String spayReceive(Map<String, String> params){
		JSONObject obj_result = new JSONObject();
		if ( params==null ) {
			obj_result.put("code", "fail");
			obj_result.put("message", "参数错误");
			return obj_result.toString();
		}

		//获取系统配置
		String spid = DicParamUtils.getParamValue("sftpayac");
		String md5key = DicParamUtils.getParamValue("sftpayackey");
		
		String userName ="";
		int userId = 0; 
		
		//获取返回参数
		String Name = params.get("Name");
		String Version = params.get("Version");
		String Charset = params.get("Charset");
		String TraceNo = params.get("TraceNo");
		String MsgSender = params.get("MsgSender");
		String SendTime = params.get("SendTime");
		String MerchantNo = params.get("MerchantNo");
		String InstCode = params.get("InstCode");
		String OrderNo = params.get("OrderNo");
		String OrderAmount = params.get("OrderAmount");
		String TransNo = params.get("TransNo");
		String TransAmount = params.get("TransAmount");
		String TransStatus = params.get("TransStatus");
		String TransType = params.get("TransType");
		String TransTime = params.get("TransTime");
		String ErrorCode = params.get("ErrorCode");
		String ErrorMsg = params.get("ErrorMsg");
		String Ext1 = params.get("Ext1");
		String SignType = params.get("SignType");
		String SignMsg = params.get("SignMsg");

		//第一步进行相关的验签操作
		StringBuffer encryptCode=new StringBuffer();
		encryptCode.append(StringUtils.isEmpty(Name)?"":Name+"|");
		encryptCode.append(StringUtils.isEmpty(Version)?"":Version+"|");
		encryptCode.append(StringUtils.isEmpty(Charset)?"":Charset+"|");
		encryptCode.append(StringUtils.isEmpty(TraceNo)?"":TraceNo+"|");
		encryptCode.append(StringUtils.isEmpty(MsgSender)?"":MsgSender+"|");
		encryptCode.append(StringUtils.isEmpty(SendTime)?"":SendTime+"|");
		encryptCode.append(StringUtils.isEmpty(InstCode)?"":InstCode+"|");
		encryptCode.append(StringUtils.isEmpty(OrderNo)?"":OrderNo+"|");
		encryptCode.append(StringUtils.isEmpty(OrderAmount)?"":OrderAmount+"|");
		encryptCode.append(StringUtils.isEmpty(TransNo)?"":TransNo+"|");
		encryptCode.append(StringUtils.isEmpty(TransAmount)?"":TransAmount+"|");
		encryptCode.append(StringUtils.isEmpty(TransStatus)?"":TransStatus+"|");
		encryptCode.append(StringUtils.isEmpty(TransType)?"":TransType+"|");
		encryptCode.append(StringUtils.isEmpty(TransTime)?"":TransTime+"|");
		encryptCode.append(StringUtils.isEmpty(MerchantNo)?"":MerchantNo+"|");
		encryptCode.append(StringUtils.isEmpty(ErrorCode)?"":ErrorCode+"|");
		encryptCode.append(StringUtils.isEmpty(ErrorMsg)?"":ErrorMsg+"|");
		encryptCode.append(StringUtils.isEmpty(Ext1)?"":Ext1+"|");
		encryptCode.append(StringUtils.isEmpty(SignType)?"":SignType+"|");

		//默认进行MD5验签操作
		String signMd5Msg= MD5.sign(encryptCode.toString(), md5key, Charset);
		UserChargeUtils uCharge =new UserChargeUtils();
		try{	
			
			//判断订单是否支付
			if (!TransStatus.equals("01")) {
				UserLogUtils.addLog(this, 0, "订单不是已支付订单，无法入账");
				obj_result.put("code", "fail");
				obj_result.put("message", "订单不是已支付订单，无法入账");
				return obj_result.toString();
			}

			//判断商户号
			if (!MerchantNo.equals(spid)) {
				UserLogUtils.addLog(this, 0, "商户编号错，返回参数：商户号【"+MerchantNo+"】，系统配置：商户号【"+spid+"】系统无法自动入帐，请与管理员联系");
				obj_result.put("code", "fail");
				obj_result.put("message", "商户编号错，系统无法自动入帐，请与管理员联系");
				return obj_result.toString();
			}
			
			//判断是否存在该条充值记录
			UserCharge usercharge = uCharge.getUserCharge(OrderNo);
			if(usercharge==null){
				UserLogUtils.addLog(this, 0, "未知单号["+OrderNo+"]，系统自动入帐失败");
				obj_result.put("code", "fail");
				obj_result.put("message", "未知单号["+OrderNo+"]，系统自动入帐失败");
				return obj_result.toString();		
			}
			
			//判断系统是否已入账
			if(usercharge.getStatus().equals("Y")){
				UserLogUtils.addLog(this, 0, "系统已经自动入帐成功，请核对");
				obj_result.put("code", "OK");
				obj_result.put("message", "系统已经自动入帐成功，请核对");
				return obj_result.toString();		
			}
			
			userName = usercharge.getUserName();
			userId = usercharge.getUserId();	
			
			//签名验证
			if(!SignMsg.equals(signMd5Msg)){
				UserLogUtils.addLog(this, userId, "签名不正确");
				obj_result.put("code", "fail");
				obj_result.put("message", "签名不正确");
				return obj_result.toString();
			}

			//金额校验
			double inAmount=Double.valueOf(OrderAmount);
			if(inAmount!=usercharge.getAmount()){
				UserLogUtils.addLog(this, userId, "系统自动入帐失败：支付金额["+usercharge.getAmount()+"]与入帐金额["+inAmount+"]不符");
				obj_result.put("code", "fail");
				obj_result.put("message", "系统自动入帐失败：支付金额["+usercharge.getAmount()+"]与入帐金额["+inAmount+"]不符");
				return obj_result.toString();
			}
			
			//入账
			FuncHandleUtils funcHandle = new FuncHandleUtils();
			String remark="网银在线支付，支付订单号【"+OrderNo+"】";
			//费率计算
			String rateStr = DicParamUtils.getParamValue(DicParamEnum.SFTPAY_RATE.getVal());
			if (StrTool.isBlankStr(rateStr)) {
				rateStr = "0";
			}
			Double rateDouble = Double.valueOf(rateStr);
			Double rate = DoubleUtil.sub(1.0, DoubleUtil.divide(rateDouble, 100.0));
			Double realAmount = DoubleUtil.mul(rate, inAmount, 2);
			Double rateAmount = DoubleUtil.sub(inAmount, realAmount, 2);
			remark += "；费率：" + String.valueOf(rateAmount);
			String result=funcHandle.FinanceAccount(userName,100,remark,OrderNo,"sftpay","",realAmount, userName, usercharge.getAgentId());
			
			//生成财务记录并更新充值记录状态
			JSONObject obj = new JSONObject();
			obj= JSONObject.fromObject(result);
			if("0".equals(obj.getString("code").trim())){

				//如果是下级代理的会员入款，需要为中间所有的代理账户入款
				if (!usercharge.getAgentId().equals(usercharge.getFromAgentUserId())) {
					this.canContinueDealOrder = false;
					this.agentId = usercharge.getAgentId();
					funcHandle.refundsForAgent(usercharge.getAgentId(), usercharge.getFromAgentUserId(), OrderNo, "sftpay", "", realAmount, userName, remark);
				}

				usercharge.setStatus("Y");
				uCharge.uptUsercharge(usercharge);
				UserLogUtils.addLog(this, userId, "网银支付成功，系统自动入账成功", usercharge.getAgentId());
				
				//更新会员等级
				/*FuncHandleUtils hand=new  FuncHandleUtils();
				hand.uptVipgrade(OrderNo);
				hand = null;*/
				
				obj_result.put("code", "OK");
				obj_result.put("message", "网银支付成功，系统自动入账成功");
			}else{
				UserLogUtils.addLog(this, userId, "在线网银支付成功，系统自动入账失败！");
				obj_result.put("code", "fail");
				obj_result.put("message", "网银在线支付成功，系统自动入账失败！");
			}	
		}catch (Exception e) {
			e.printStackTrace();
			UserLogUtils.addLog(this, userId, "内部错误:" + e.getMessage());
			obj_result.put("code", "fail");
			obj_result.put("message", "内部错误：" + e.getMessage());
		}
		return obj_result.toString();
	}
	
	//MD5加密函数
	static class MD5 {

		public static final String SIGN_ALGORITHMS = "MD5";

		/**
		 * 使用{@code MD5}方式对字符串进行签名
		 * @param text 需要加签名的数据
		 * @param key 对需要签名的的数据进行加盐
		 * @param charset 数据的编码方式
		 * @return 返回签名信息
		 */
		public static String sign(String text, String key, String charset) {
			String message = text + key;

			MessageDigest digest = getDigest(SIGN_ALGORITHMS);
			digest.update(getContentBytes(message, charset));

			byte[] signed = digest.digest();

			return toHexString(signed);
		}

		/**
		 * 使用{@code MD5}方式对签名信息进行验证
		 * @param text 需要加签名的数据
		 * @param sign 签名信息
		 * @param key 对需要签名的的数据进行加盐
		 * @param charset 数据的编码方式
		 * @return 是否验证通过。{@code True}表示通过
		 */
		public static boolean verify(String text, String sign, String key, String charset) {
			String mysign = sign(text, key, charset);

			if (mysign.equals(sign)) {
				return true;
			} else {
				return false;
			}
		}

		/**
		 * 返回实现指定摘要算法的 {@code MessageDigest} 对象。
		 * @param algorithm 信息摘要算法名称
		 * @return 返回摘要算法对象
		 */
		private static MessageDigest getDigest(String algorithm) {
			try {
				return MessageDigest.getInstance(algorithm);
			} catch (final NoSuchAlgorithmException ex) {
				throw new IllegalArgumentException("Not support:" + algorithm,
						ex);
			}
		}

		/**
		 * 使用给定的 charset 将此 String 编码到 byte 序列，并将结果存储到新的 byte 数组。
		 * @param content 字符串对象
		 * @param charset 编码方式
		 * @return 所得 byte 数组
		 */
		public static byte[] getContentBytes(String content, String charset) {
			if (charset == null || "".equals(charset)) {
				return content.getBytes();
			}

			try {
				return content.getBytes(charset);
			} catch (UnsupportedEncodingException ex) {
				throw new IllegalArgumentException("Not support:" + charset, ex);
			}
		}

		/**
		 * 把{@code Byte}数组转成十六进制格式的字符串
		 * @param value 需要转换的字节数组
		 * @return 返回转换后的{@code String}对象
		 */
		public static String toHexString(byte[] value) {
			if (value == null) {
				return null;
			}

			StringBuffer sb = new StringBuffer(value.length * 2);
			for (int i = 0; i < value.length; i++) {
				sb.append(toHexString(value[i]));
			}
			return sb.toString();
		}

		/**
		 * 把{@code Byte}类型转成十六进制格式的字符串
		 * @param value 需要转换的值
		 * @return 返回转换后的{@code String}对象
		 */
		public static String toHexString(byte value) {
			String hex = Integer.toHexString(value & 0xFF);

			return padZero(hex, 2);
		}

		/**
		 * 使用"0"左补齐字符串
		 * 
		 * @param hex 十六进制字符串
		 * 
		 * @param length  字符串的固定长度
		 * 
		 * @return 返回补齐后的十六进制字符串
		 */
		private static String padZero(String hex, int length) {
			for (int i = hex.length(); i < length; i++) {
				hex = "0" + hex;
			}
			return hex.toUpperCase();
		}
	}
}
